package com.chinamobile.iot.utils;

import com.chinamobile.iot.config.MqttConfiguration;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.security.auth.x500.X500PrivateCredential;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Date;

/**
 * Created on 2016/12/29.
 *
 * @author stayfool
 */
@Component
public class CertificateUtil {

    public static String provider = "BC";
    public static String algorithm = "RSA";
    public static String signatureAlgorithm = "SHA1withRSA";
    public static int keySize = 2048;
    public static String ALIAS = "mqttserver";

    private static MqttConfiguration prop;

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    @Autowired
    public CertificateUtil(MqttConfiguration prop) {
        CertificateUtil.prop = prop;
    }

    /**
     * Create a KeyStore containing the a private credential with
     * certificate chain and a trust anchor.
     */
    public static KeyStore createKeyStore()
            throws Exception {
        KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());

        store.load(null, null);

        X500PrivateCredential credential = createCredential();
        store.setCertificateEntry(credential.getAlias(), credential.getCertificate());
        store.setKeyEntry(credential.getAlias(), credential.getPrivateKey(), prop.getSsl().getPassword().toCharArray(),
                new Certificate[]{credential.getCertificate(), credential.getCertificate(), credential.getCertificate()});

        return store;
    }

    /**
     * Create a X500PrivateCredential for the end entity.
     */
    public static X500PrivateCredential createCredential() {

        KeyPair keyPair = generateRSAKeyPair();
        if (keyPair == null)
            return null;

        X509Certificate cert = buildX509V3Cert(keyPair);

        return new X500PrivateCredential(cert, keyPair.getPrivate(), ALIAS);
    }

    public static X509Certificate buildX509V3Cert(KeyPair keyPair) {
        try {

            X500NameBuilder builder = new X500NameBuilder();
            builder.addRDN(BCStyle.CN, "com.chinamobile.iot.10086.com");
            builder.addRDN(BCStyle.O, "10086.com");
            builder.addRDN(BCStyle.OU, "10086.com");
            builder.addRDN(BCStyle.ST, "Chongqing");
            builder.addRDN(BCStyle.L, "Chongqing");
            builder.addRDN(BCStyle.C, "China");
            X500Name x500Name = builder.build();

            X509v3CertificateBuilder certBldr = new JcaX509v3CertificateBuilder(
                    x500Name,
                    BigInteger.valueOf(1),
                    new Date(System.currentTimeMillis()),
                    new Date(System.currentTimeMillis() + prop.getSsl().getPeriod() * 24 * 60 * 60 * 1000),
                    x500Name, keyPair.getPublic()
            );

            JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
            certBldr
                    .addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(keyPair.getPublic()))
                    .addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(keyPair.getPublic()))
                    .addExtension(Extension.basicConstraints, true, new BasicConstraints(false))
                    .addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment));

            ContentSigner signer = new JcaContentSignerBuilder(signatureAlgorithm).setProvider(provider).build(keyPair.getPrivate());

            return new JcaX509CertificateConverter().setProvider(provider).getCertificate(certBldr.build(signer));
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * Create a random 2048 bit RSA key pair
     */
    public static KeyPair generateRSAKeyPair() {
        KeyPairGenerator kpGen = null;
        try {
            kpGen = KeyPairGenerator.getInstance(algorithm, provider);
            kpGen.initialize(keySize, new SecureRandom());
            return kpGen.generateKeyPair();
        } catch (Exception e) {
        }
        return null;
    }

}
